package com.gitee.weixin.mp.config;

import com.gitee.weixin.mp.domain.MpConfig;
import com.gitee.weixin.mp.handler.*;
import com.gitee.weixin.mp.handler.msg.*;
import com.gitee.weixin.mp.service.MpConfigService;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import me.chanjar.weixin.common.api.WxConsts.EventType;
import me.chanjar.weixin.common.api.WxConsts.MenuButtonType;
import me.chanjar.weixin.common.api.WxConsts.XmlMsgType;
import me.chanjar.weixin.mp.api.WxMpConfigStorage;
import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.constant.WxMpEventConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.*;

/**
 * wechat mp configuration
 *
 * @author Binary Wang(https://github.com/binarywang)
 */
@Configuration
@ConditionalOnClass(WxMpService.class)
public class WechatMpConfiguration {
    @Autowired
    protected LogHandler              logHandler;
    @Autowired
    protected MenuLinkHandler         menulinkHandler;
    @Autowired
    protected KfSessionHandler        kfSessionHandler;
    @Autowired
    protected StoreCheckNotifyHandler storeCheckNotifyHandler;
    @Autowired
    protected LocationHandler         locationHandler;
    @Autowired
    protected MenuHandler             menuHandler;
    @Autowired
    protected UnsubscribeHandler      unsubscribeHandler;
    @Autowired
    protected SubscribeHandler        subscribeHandler;
    @Autowired
    protected ScanHandler             scanHandler;

    @Autowired
    protected AbstractWxMsgHandler      imageMsgHandler;
    @Autowired
    protected LinkMsgHandler            linkMsgHandler;
    @Autowired
    protected LocationMsgHandler        locationMsgHandler;
    @Autowired
    protected ShortvideoVideoMsgHandler shortvideoVideoMsgHandler;
    @Autowired
    protected TextMsgHandler            textMsgHandler;
    @Autowired
    protected VideoMsgHandler           videoMsgHandler;
    @Autowired
    protected VoiceMsgHandler           voiceMsgHandler;
    @Autowired
    protected MpConfigService           mpConfigSrv;

    @Bean
    @ConditionalOnMissingBean
    public MpConfig getConfig() {
        MpConfig config = mpConfigSrv.getConfig();
        return config;
    }

    @Bean
    @ConditionalOnMissingBean
    public WxMpConfigStorage configStorage(MpConfig config) {
        WxMpInMemoryConfigStorage configStorage = new WxMpInMemoryConfigStorage();

        if (config != null) {
            configStorage.setAppId(config.getAppId());
            configStorage.setSecret(config.getSecret());
            configStorage.setToken(config.getToken());
            configStorage.setAesKey(config.getAesKey());
        }

        return configStorage;
    }

    @Bean
    @ConditionalOnMissingBean
    public WxMpService wxMpService(WxMpConfigStorage configStorage) {
        // WxMpService wxMpService = new
        // me.chanjar.weixin.mp.api.impl.okhttp.WxMpServiceImpl();
        // WxMpService wxMpService = new
        // me.chanjar.weixin.mp.api.impl.jodd.WxMpServiceImpl();
        // WxMpService wxMpService = new
        // me.chanjar.weixin.mp.api.impl.apache.WxMpServiceImpl();
        // WxMpService wxMpService = new
        // me.chanjar.weixin.mp.api.impl.WxMpServiceImpl();

        // 基于数据库事务实现的可以在集群环境使用的accesstoken服务
        WxMpService wxMpService = new WxMpWithDBLockService(this.mpConfigSrv);

        wxMpService.setWxMpConfigStorage(configStorage);

        return wxMpService;
    }

    @Bean
    public WxMpMessageRouter router(WxMpService wxMpService) {
        // 仅用于异步执行的router目前只有logHandler
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("WxMpMessageRouter-pool-%d").build();
        ExecutorService pool = new ThreadPoolExecutor(5, 5,
                                                      1000L, TimeUnit.MILLISECONDS,
                                                      new LinkedBlockingQueue<Runnable>(20), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

        final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService);
        newRouter.setExecutorService(pool);


        // newRouter.setMessageDuplicateChecker(messageDuplicateChecker);
        // newRouter.setSessionManager(sessionManager);
        // 可以在这里设置消息排重方法，如果使用了会话保持的负载横策略，集群环境下也可以使用内存DuplicateChecker
        // 但如果没有会话保持的集群环境，则必须采用redis等方式来实现排重
        // 会话保持建议用openid
        // sessionManager 亦同 DuplicateChecker

        // 记录所有事件的日志 （异步执行）
        newRouter.rule().handler(this.logHandler).next();

        // 接收客服会话管理事件
        newRouter.rule().async(false).msgType(XmlMsgType.EVENT).event(WxMpEventConstants.CustomerService.KF_CREATE_SESSION)
                .handler(this.kfSessionHandler).end();
        newRouter.rule().async(false).msgType(XmlMsgType.EVENT).event(WxMpEventConstants.CustomerService.KF_CLOSE_SESSION)
                .handler(this.kfSessionHandler).end();
        newRouter.rule().async(false).msgType(XmlMsgType.EVENT).event(WxMpEventConstants.CustomerService.KF_SWITCH_SESSION)
                .handler(this.kfSessionHandler).end();

        // 门店审核事件
        newRouter.rule().async(false).msgType(XmlMsgType.EVENT).event(WxMpEventConstants.POI_CHECK_NOTIFY).handler(this.storeCheckNotifyHandler)
                .end();

        // 自定义菜单事件
        newRouter.rule().async(false).msgType(XmlMsgType.EVENT).event(MenuButtonType.CLICK).handler(this.menuHandler).end();

        // 点击菜单连接事件
        newRouter.rule().async(false).msgType(XmlMsgType.EVENT).event(MenuButtonType.VIEW).handler(this.menulinkHandler).end();

        // 关注事件
        newRouter.rule().async(false).msgType(XmlMsgType.EVENT).event(EventType.SUBSCRIBE).handler(this.subscribeHandler).end();

        // 取消关注事件
        newRouter.rule().async(false).msgType(XmlMsgType.EVENT).event(EventType.UNSUBSCRIBE).handler(this.unsubscribeHandler).end();

        // 上报地理位置事件
        newRouter.rule().async(false).msgType(XmlMsgType.EVENT).event(EventType.LOCATION).handler(this.locationHandler).end();

        // 扫码事件
        newRouter.rule().async(false).msgType(XmlMsgType.EVENT).event(EventType.SCAN).handler(this.scanHandler).end();

        // 文本消息
        newRouter.rule().async(false).msgType(XmlMsgType.TEXT).handler(this.textMsgHandler).end();

        // 接收地理位置消息
        newRouter.rule().async(false).msgType(XmlMsgType.LOCATION).handler(this.locationMsgHandler).end();

        // 接收图片消息
        newRouter.rule().async(false).msgType(XmlMsgType.IMAGE).handler(this.imageMsgHandler).end();

        // 接收转发链接消息
        newRouter.rule().async(false).msgType(XmlMsgType.LINK).handler(this.linkMsgHandler).end();

        // 接收到视频消息
        newRouter.rule().async(false).msgType(XmlMsgType.VIDEO).handler(this.videoMsgHandler).end();

        // 接收到小视频消息
        newRouter.rule().async(false).msgType(XmlMsgType.SHORTVIDEO).handler(this.shortvideoVideoMsgHandler).end();

        // 接收到语音消息
        newRouter.rule().async(false).msgType(XmlMsgType.VOICE).handler(this.voiceMsgHandler).end();

        return newRouter;
    }

}
